﻿using System;
using System.Collections.Generic;
using System.Drawing;
using SdmxMl.Common;
using SdmxMl.Manager;
using SdmxMl.Structure;
using Telerik.WinControls;
using Telerik.WinControls.UI;
using SdmxDiff;

namespace ChopPlugin
{

    /// <summary> Manages grid with CHOP content for export in Excel </summary>
    public class ExportGridCreator
    {
        #region Internal Members
        private const string DATE_FORMATTING = "dd.MM.yyyy";
        private const string YES_TEXT = "oui";
        private const string NO_TEXT = "non";
        private const string DIFF_MARK = "OFS";
        private const string LATERAL_TEXT = "Lateral";
       

        /// <summary>Grid control holding CHOP codes </summary>
        private RadGridView gridView;
        /// <summary>CHOP codes list dictionary</summary>
        private Dictionary<string, CodeListType> dico;
        private Dictionary<string, CodeListType> dicoOld;
        /// <summary>SDMX artefacts loader & container</summary>
        private SmManager sm;

        private List<ReferenceDiagnostic> diagList;

        #endregion

        #region Constructor

        public ExportGridCreator(RadGridView radGrid)
        {
            gridView = radGrid;

            gridView.ToolTipTextNeeded += new ToolTipTextNeededEventHandler(gridView_ToolTipTextNeeded);
        }
        #endregion

        #region ToolTip needed Event

        /// <summary>
        /// By default provides a tooltip in cell content is not fully displayed
        /// </summary>
        /// <param name="sender"></param><param name="e"></param>
        private void gridView_ToolTipTextNeeded(object sender, Telerik.WinControls.ToolTipTextNeededEventArgs e)
        {
            GridDataCellElement dataCell = sender as GridDataCellElement;

            if (dataCell != null)
            {
                TextPart textPart = new TextPart(dataCell);
                SizeF size = textPart.Measure(new SizeF(float.PositiveInfinity, float.PositiveInfinity));
                SizeF sizeInCell = textPart.Measure(new SizeF(dataCell.ColumnInfo.Width, float.PositiveInfinity));

                string toolTipText = null;

                float cellWidth = dataCell.ColumnInfo.Width - dataCell.BorderWidth * 2;
                float cellHeight = dataCell.Size.Height - dataCell.BorderWidth * 2;

                if (size.Width > cellWidth || cellHeight < sizeInCell.Height)
                {
                    toolTipText = dataCell.Text;
                }

                e.ToolTipText = toolTipText;
            }
        }
        #endregion

        #region Grid Columns construction

        /// <summary>
        /// Create Columns for multilang grid and fill with hierarchy content
        /// </summary>
        /// <param name="hcl">the hierarchical CHOP artefact</param>
        /// <param name="enableFilter">True means filter is enabled on columns</param>
        public void LoadGridData(HierarchicalCodelistType hcl, bool enableFilter)
        {
            using (new SdmxMl.Helper.CWaitCursor())
            {
                gridView.BeginUpdate();

                gridView.MasterTemplate.Columns.Clear();
                gridView.EnableFiltering = enableFilter;
                gridView.EnableAlternatingRowColor = true;
                gridView.MasterTemplate.AllowColumnChooser = false;

                GridViewTextBoxColumn colText = new GridViewTextBoxColumn("Code");
                colText.AllowResize = true;
                colText.AllowReorder = false;
                colText.Width = 70;
                gridView.MasterTemplate.Columns.Add(colText);

                colText = new GridViewTextBoxColumn("Type");
                colText.AllowResize = true;
                colText.AllowReorder = false;
                colText.Width = 40;
                gridView.MasterTemplate.Columns.Add(colText);

                colText = new GridViewTextBoxColumn("Level");
                colText.AllowResize = true;
                colText.AllowReorder = false;
                colText.Width = 40;
                gridView.MasterTemplate.Columns.Add(colText);

                foreach (string lg in LangSupport.SupportedLanguages)
                {
                    colText = new GridViewTextBoxColumn(lg.ToUpper());
                    colText.AllowResize = true;
                    colText.AllowReorder = false;
                    colText.Width = 200;
                    gridView.MasterTemplate.Columns.Add(colText);
                }
                gridView.MasterTemplate.Columns["EN"].Width = 30;

             //   GridViewCheckBoxColumn colCheck = new GridViewCheckBoxColumn("Lateralite");
             //   gridView.MasterTemplate.Columns.Add(colCheck);
                colText = new GridViewTextBoxColumn("Lateralite");
                colText.AllowResize = true;
                colText.AllowReorder = false;
                colText.Width = 70;
                gridView.MasterTemplate.Columns.Add(colText);


                gridView.EndUpdate();
                
                LoadUnboundData(hcl);
            }

        }
        #endregion

        #region Filling Multilang Grid with CHOP version

        public void LoadUnboundData(HierarchicalCodelistType hcl)
        {
            using (gridView.DeferRefresh())
            {
                sm = ReferenceManager.GetSmContainerImage();
                ArtefactRefTypedList artl = hcl.ArtefactRefList;
                sm.LoadMissingArtefactsFromReferenceMgr(artl);
                sm.LoadMissingArtefactsFromRegistry(artl);

                dico = PdfChop.GetRefDictionary(sm, hcl.HierarchyList[0]);

                gridView.MasterTemplate.Rows.Clear();

                foreach (CodeRefType it in hcl.HierarchyList[0].CodeRefList)
                    FillRow(it, 1);

           }
        }

        private void FillRow(CodeRefType crt, int level)
        {
            CodeListType clType = dico[crt.CodelistAliasRef];
            ISdmxHierarchyListItem item = clType.CodeList.LocateItem(crt.CodeID);

            if (item == null)
                throw new ApplicationException("Unable to proceed. Hierarchy referred code Id '" +
                    crt.CodeID + "' is not present in codelist: " + clType.FullIdent);

            CodeType code = item as CodeType;
            LoadCodeInGrid(code, level);
            // Recurse
            foreach (CodeRefType it in crt.CodeRefList)
                FillRow(it, level + 1);
        }

        private void LoadCodeInGrid(CodeType code, int level)
        {
            bool lateral = code.AnnotationList.GetAnnotationType(ChopCodes.ANNOT_TYPE_LATERAL) != null;
            List<string> listText = GetText(code.Name);

            // first line code attributes
            gridView.MasterTemplate.Rows.Add(
                PdfChop.BeautifyCodeId(code.Id),
                "T",
                level.ToString(),
                listText[0],
                listText[1],
                listText[2],
                listText[3],
                lateral ? LATERAL_TEXT : string.Empty);
                    


            // Supplementary lines for each annotation
            foreach (Annotation a in code.AnnotationList)
            {
                if (a.Type == ChopCodes.ANNOT_TYPE_LATERAL)
                    continue;
                listText = GetText(a.Text);
                gridView.MasterTemplate.Rows.Add(
                    PdfChop.BeautifyCodeId(code.Id),
                    GetExportTypeOfAnnotation(a),
                    level.ToString(),
                    listText[0],
                    listText[1],
                    listText[2],
                    listText[3],
                    string.Empty);
            }
        }

        /// <summary>Convert a SDMX multilang to a list of texts</summary>
        /// <param name="ml"></param>
        /// <returns>the list of texts ordered according to language set</returns>
        private List<string> GetText(MultiLang ml)
        {
             List<string> listText = new List<string>();
            foreach (string lg in LangSupport.SupportedLanguages)
                listText.Add(ml.GetForLanguage(lg));
            return listText;
          
        }

        /// <summary>Translates SDMX annotation type to CHOP row type</summary>
        /// <param name="a">the sdmx annotation</param>
        /// <returns>the CHOP type or empty if no corresponding found</returns>
        public static string GetExportTypeOfAnnotation(Annotation a)
        {
            string t = string.Empty;
            switch (a.Type)
            {
                case ChopCodes.ANNOT_TYPE_EXCLUSION:
                    t = "X"; break;
                case ChopCodes.ANNOT_TYPE_REMARK:
                    t = "B"; break;
                case ChopCodes.ANNOT_TYPE_HINT:
                    t = "S"; break;
                case ChopCodes.ANNOT_TYPE_NOTE:
                    t = "N"; break;
                case ChopCodes.ANNOT_TYPE_INCLUDE:
                    t = "I"; break;
            }
            return t;
        }

        #endregion

        #region Grid CSV report construction

        /// <summary>
        /// Defines columns used for CSV differences view. then fill content
        /// </summary>
        /// <param name="hclNew">new chop version of hierarchy</param>
        /// <param name="hclOld">Old Chop version of hierarchy</param>
        /// <param name="enableFilter">if true filter is enabled on all columns</param>
        public void LoadGridCsvReport(HierarchicalCodelistType hclNew, HierarchicalCodelistType hclOld, bool enableFilter)
        {
            using (new SdmxMl.Helper.CWaitCursor())
            {
                gridView.BeginUpdate();

                gridView.MasterTemplate.Columns.Clear();
                gridView.EnableFiltering = enableFilter;
                gridView.EnableAlternatingRowColor = true;
                gridView.MasterTemplate.AllowColumnChooser = false;

                GridViewTextBoxColumn colText = new GridViewTextBoxColumn("nbchar");
                colText.AllowResize = true;
                colText.AllowReorder = false;
                colText.TextAlignment = ContentAlignment.MiddleCenter; 
                colText.Width = 50;
                gridView.MasterTemplate.Columns.Add(colText);

                colText = new GridViewTextBoxColumn("zcode");
                colText.AllowResize = true;
                colText.AllowReorder = false;
                colText.Width = 70;
                gridView.MasterTemplate.Columns.Add(colText); 

                colText = new GridViewTextBoxColumn("item type");
                colText.AllowResize = true;
                colText.AllowReorder = false;
                colText.TextAlignment = ContentAlignment.MiddleCenter;
                colText.Width = 40;
                gridView.MasterTemplate.Columns.Add(colText);

                colText = new GridViewTextBoxColumn("text");
                colText.AllowResize = true;
                colText.AllowReorder = false;
                colText.Width = 300;
                gridView.MasterTemplate.Columns.Add(colText);

             //   GridViewCheckBoxColumn colCheck = new GridViewCheckBoxColumn("codable");
             //   gridView.MasterTemplate.Columns.Add(colCheck);
                colText = new GridViewTextBoxColumn("codable");
                colText.AllowResize = true;
                colText.AllowReorder = false;
                colText.TextAlignment = ContentAlignment.MiddleCenter;
                colText.Width = 60;
                gridView.MasterTemplate.Columns.Add(colText);


                colText = new GridViewTextBoxColumn("emitter");
                colText.AllowResize = true;
                colText.AllowReorder = false;
                colText.TextAlignment = ContentAlignment.MiddleCenter;
                colText.Width = 60;
                gridView.MasterTemplate.Columns.Add(colText);

                colText = new GridViewTextBoxColumn("status");
                colText.AllowResize = true;
                colText.AllowReorder = false;
                colText.TextAlignment = ContentAlignment.MiddleCenter;
                colText.Width = 60;
                gridView.MasterTemplate.Columns.Add(colText);

                colText = new GridViewTextBoxColumn("modification date");
                colText.AllowResize = true;
                colText.AllowReorder = false;
                colText.Width = 80;
                gridView.MasterTemplate.Columns.Add(colText);

                colText = new GridViewTextBoxColumn("indent level");
                colText.AllowResize = true;
                colText.AllowReorder = false;
                colText.TextAlignment = ContentAlignment.MiddleCenter;
                colText.Width = 70;
                gridView.MasterTemplate.Columns.Add(colText);


               // colCheck = new GridViewCheckBoxColumn("lateralite");
               // gridView.MasterTemplate.Columns.Add(colCheck);

                colText = new GridViewTextBoxColumn("lateralite");
                colText.AllowResize = true;
                colText.AllowReorder = false;
                colText.TextAlignment = ContentAlignment.MiddleCenter;
                colText.Width = 60;
                gridView.MasterTemplate.Columns.Add(colText);

                colText = new GridViewTextBoxColumn("permutation");
                colText.AllowResize = true;
                colText.AllowReorder = false;
                colText.TextAlignment = ContentAlignment.MiddleCenter;
                colText.Width = 60;
                gridView.MasterTemplate.Columns.Add(colText);

                gridView.EndUpdate();

                LoadCsvDiffData(hclNew, hclOld);
            }

        }

        public void LoadCsvDiffData(HierarchicalCodelistType hclNew, HierarchicalCodelistType hclOld)
        {
            using (gridView.DeferRefresh())
            {
                sm = ReferenceManager.GetSmContainerImage();
                ArtefactRefTypedList artl = hclNew.ArtefactRefList;
                sm.LoadMissingArtefactsFromReferenceMgr(artl);
                sm.LoadMissingArtefactsFromRegistry(artl);

                dico = PdfChop.GetRefDictionary(sm, hclNew.HierarchyList[0]);

                artl = hclOld.ArtefactRefList;
                sm.LoadMissingArtefactsFromReferenceMgr(artl);
                sm.LoadMissingArtefactsFromRegistry(artl);

                Dictionary<string, CodeListType> dicoPrevious = PdfChop.GetRefDictionary(sm, hclOld.HierarchyList[0]);

                if (dico.Count != dicoPrevious.Count)
                    throw new ApplicationException("Unsupported CHOP versions have different level count.");
                dicoOld = new Dictionary<string, CodeListType>();
                foreach (string alias in dico.Keys)
                {
                    if (dicoPrevious.ContainsKey(alias) == false)
                        throw new ApplicationException("Unsupported CHOP versions have different alias naming.");
                    if (dicoPrevious[alias].FullIdent != dico[alias].FullIdent)
                        dicoOld.Add(alias, dicoPrevious[alias]);
                    else
                        dicoOld.Add(alias, null);

                }

                // prepare list of changed codelists

                gridView.MasterTemplate.Rows.Clear();

                foreach (CodeRefType it in hclNew.HierarchyList[0].CodeRefList)
                    FillCsvRow(it, 1);

           }
        }


        /// <summary>Creates a CSV row info in grid </summary>
        /// <param name="crt">hierarchy item info</param>
        /// <param name="level">level in hierarchy</param>
        private void FillCsvRow(CodeRefType crt, int level)
        {
            CodeListType clType = dico[crt.CodelistAliasRef];
            ISdmxHierarchyListItem item = clType.CodeList.LocateItem(crt.CodeID);

            if (item == null)
                throw new ApplicationException("Unable to proceed. Hierarchy referred code Id '" +
                    crt.CodeID + "' is not present in codelist: " + clType.FullIdent);

            CodeType code = item as CodeType;
            CodeType codeOld = dicoOld[crt.CodelistAliasRef] == null ? code : dicoOld[crt.CodelistAliasRef].CodeList.LocateItem(code.Id) as CodeType;
            LoadCsvCodeInGrid(code, codeOld, level, crt.CodeRefList.Count > 0);
            // Recurse
            foreach (CodeRefType it in crt.CodeRefList)
                FillCsvRow(it, level + 1);
        }

        private void LoadCsvCodeInGrid(CodeType code, CodeType codeOld, int level, bool hasChild)
        {
            bool lateral = code.AnnotationList.GetAnnotationType(ChopCodes.ANNOT_TYPE_LATERAL) != null;
            string nbChar = code.Id.Length.ToString();
            string codeStart = level == 1 ? "C" : "Z";
            string BeautyCode = codeStart + PdfChop.BeautifyCodeId(code.Id);

            // codeText is modified ? 
            DiffType diff = codeOld == null ? DiffType.New : DiffType.None;
            if (codeOld != null && codeOld != code && code.Name.ToString().CompareTo(codeOld.Name.ToString()) != 0)
                diff = DiffType.Modified;

            // By default, Codable information is "oui" if code has no child, no otherwise
            // THis can be overriden by an annotation of type CODABLE
            string codable = hasChild ? NO_TEXT : YES_TEXT;
            Annotation ac = code.AnnotationList.GetAnnotationType(ChopCodes.ANNOT_TYPE_CODABLE);
            if (ac != null)
                codable = ac.Title == YES_TEXT ? YES_TEXT : NO_TEXT;
            
            // Has annotations diff
            AnnotationListDiff anlDiff = null;
            if (codeOld != null && codeOld != code)
            {
                anlDiff = new AnnotationListDiff(codeOld.AnnotationList, code.AnnotationList, LangSupport.CurLang);

                // If Type T has no change, check Lateral and tell change of type 2 if Lateralite change
                if (diff == DiffType.None &&
                    codeOld.AnnotationList.GetAllAnnotationOfType(ChopCodes.ANNOT_TYPE_LATERAL, null).Count !=
                    code.AnnotationList.GetAllAnnotationOfType(ChopCodes.ANNOT_TYPE_LATERAL, null).Count)
                {
                    diff = DiffType.Modified;
                }
            }


            // first line code text
            gridView.MasterTemplate.Rows.Add(
                nbChar,
                BeautyCode,
                "T",
                code.Name.ToString(),
                codable,
                diff != DiffType.None ? DIFF_MARK : string.Empty,
                ((int)diff).ToString(),
                diff != DiffType.None ? DateTime.Now.ToString(DATE_FORMATTING) : string.Empty,
                level.ToString(),
                lateral ? LATERAL_TEXT : string.Empty,
                Permutation.HasPermutationInCurrentLang(code.Uri) ? YES_TEXT : string.Empty
                );
                    

            // Supplementary lines for each annotation
            foreach (Annotation a in code.AnnotationList)
            {
                if (a.Type == ChopCodes.ANNOT_TYPE_LATERAL)
                    continue;
                if (a.Text.ToString().Length == 0) // nothing in current language
                    continue;
                // reset state
                diff = codeOld == null ? DiffType.New : DiffType.None;
                if (anlDiff != null)
                    diff = anlDiff.DicoDiff.NewDiffKind(a.Type, a);


                // Force annotation diff to 'NEW' (IT demand 30.07.2013)
                gridView.MasterTemplate.Rows.Add(
                nbChar,
                BeautyCode,
                GetExportTypeOfAnnotation(a),
                a.Text.ToString(),
                NO_TEXT,
                diff != DiffType.None ? DIFF_MARK : string.Empty,
                diff != DiffType.None ?((int)DiffType.New).ToString() : ((int)DiffType.None).ToString(),
                diff != DiffType.None ? DateTime.Now.ToString(DATE_FORMATTING) : string.Empty,
                level.ToString(),
                string.Empty,
                a.Url != null && Permutation.HasPermutationInCurrentLang(a.Url.ToString()) ? YES_TEXT : string.Empty
                );
            }
        }
        #endregion

        #region Checking Refered codes

        public void CheckreferedCodes(HierarchicalCodelistType hcl)
        {
            sm = ReferenceManager.GetSmContainerImage();
            ArtefactRefTypedList artl = hcl.ArtefactRefList;
            sm.LoadMissingArtefactsFromReferenceMgr(artl);
            sm.LoadMissingArtefactsFromRegistry(artl);
            diagList = new List<ReferenceDiagnostic>(1024);
            checkCount = 0;
            dico = PdfChop.GetRefDictionary(sm, hcl.HierarchyList[0]);

            foreach (CodeRefType it in hcl.HierarchyList[0].CodeRefList)
                CheckRow(it);

            RadMessageBox.Show(checkCount.ToString() + " References checked.", "References checking", System.Windows.Forms.MessageBoxButtons.OK, RadMessageIcon.Info);

            // Now display grid of diff diag
            DlgRefCheck aDlg = new DlgRefCheck();
            aDlg.BindSource(diagList);
            aDlg.ShowDialog();


        }

        int checkCount;

        private void CheckRow(CodeRefType crt)
        {
            CodeListType clType = dico[crt.CodelistAliasRef];
            ISdmxHierarchyListItem item = clType.CodeList.LocateItem(crt.CodeID);

            if (item == null)
                throw new ApplicationException("Unable to proceed. Hierarchy referred code Id '" +
                    crt.CodeID + "' is not present in codelist: " + clType.FullIdent);

            CodeType code = item as CodeType;
            CheckCodeRef(code);
            // Recurse
            foreach (CodeRefType it in crt.CodeRefList)
                CheckRow(it);
        }

        private void CheckCodeRef(CodeType code)
        {
            AnnotationList anl = code.AnnotationList.GetAllAnnotationOfType(ChopCodes.ANNOT_TYPE_HINT, null);
            anl.AddRange(code.AnnotationList.GetAllAnnotationOfType(ChopCodes.ANNOT_TYPE_EXCLUSION, null));

            foreach (Annotation a in anl)
            {
                List<string> listRefCodes = ChopRefParser.GetListOfReferedCode(a.Text.ToString());
                checkCount += listRefCodes.Count;
                foreach (string codeId in listRefCodes)
                {
                    // dico index according to code length
                    int index = codeId.Length;
                    if (index < 1 || index > dico.Count)
                        throw new ApplicationException("Code: " + code.Id + " Invalid reference code received. '" + codeId + "'");

                    // Alias renamed in sdmx 2.1 library
                    string alias = index.ToString();
                    if (SdmxMl.Multiplexor.Mux.IsNewSmsRegistry)
                        alias = ChopCodes.ALIAS_BASE + (index - 1).ToString();

                    ISdmxHierarchyListItem item = dico[alias].CodeList.LocateItem(codeId);
                    ReferenceDiagnostic diag = new ReferenceDiagnostic(true, code.Id, codeId, a);
                    if (item == null)
                        diag.Found = false;
                       // throw new ApplicationException("Unable to proceed. Annotation referred code Id '" +
                       //     codeId + "' is not present. Source Code: " + code.Id + " Annotation: " + a.Text.ToString());

                    diagList.Add(diag);
                }
            }

        }

        #endregion

        #region Double Click Code navigation

        public void NavigateToCode(GridDataCellElement dataCell)
        {
            if (dataCell != null && dataCell.Value != null && 
                (dataCell.ColumnInfo.Name == "Code" || dataCell.ColumnInfo.Name == "zcode"))
            {
                string zcode = dataCell.Value.ToString().Replace("Z", "").Replace(".", "");
                int nb = zcode.Length;
                if (nb > 0 && nb <= dico.Count)
                {
                    CodeListType clType = dico[nb.ToString()];
                    CodeType item = clType.CodeList.LocateItem(zcode) as CodeType;
                    NavigateTodetail(UrnManager.FullIdToUrn(SdmxArtefactType.CodeLists.ToString(),
                        clType.FullIdent, item, null));
                }
            }
        }
        private void NavigateTodetail(string urn)
        {
            if (ReferenceManager.TreeMgr.PanelDetail != null)
                ReferenceManager.TreeMgr.PanelDetail.TabControlDetail.SelectedIndex = 0;

            // If already in memory, navigate otherwhile get from registry
            UrnParser p = new UrnParser(urn);
            if (p.IsSupported)
            {
                // If already in memory, navigate otherwhile get from regidtry
                if (NavigationManager.LocatenavigateProject(p.ArType, p.FullIdent, p) == null)
                    NavigationManager.NavigateWithRegistry(p.ArType, p.FullIdent, true, p);
            }
        }
        #endregion

    }
}
